package com.pt25.service.sys.domain.config.service.impl;

import cn.hutool.json.JSONObject;
import com.pt25.base.dep.db.DbConstants;
import com.pt25.base.dep.redis.RedisService;
import com.pt25.base.exception.BizException;
import com.pt25.base.model.common.constants.RedisConstant;
import com.pt25.base.util.JsonUtil;
import com.pt25.base.util.PageUtil;
import com.pt25.base.util.StrUtil;
import com.pt25.service.sys.domain.config.constants.ConfigConstant;
import com.pt25.service.sys.domain.config.ddo.ConfigData;
import com.pt25.service.sys.domain.config.ddo.ConfigValue;
import com.pt25.service.sys.domain.config.enums.DataTypeEnum;
import com.pt25.service.sys.domain.config.enums.ValueTypeEnum;
import com.pt25.service.sys.domain.config.service.ConfigValueService;
import com.pt25.service.sys.repository.mysql.persistence.ConfigValueRepo;
import com.pt25.service.sys.repository.mysql.po.ConfigValuePo;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.client.codec.StringCodec;
import org.springframework.stereotype.Service;

import java.util.*;

@Slf4j
@Service
public class ConfigValueServiceImpl implements ConfigValueService {
    @Resource
    private ConfigValueRepo configValueRepo;

    @Resource
    private RedisService redisService;

    @Override
    public Long addConfigValue(ConfigValue configValue, ConfigData configData) {
        if (checkConfigValue(configValue, configData)) {
            Long id = addConfigValueToDb(configValue, configData);
            saveOrCleanConfigValueCache(id, configData);
            return id;
        }
        return 0L;
    }

    @Override
    public Boolean delConfigValue(ConfigValue configValue, Object valueContent, ConfigData configData) {
        if (null == valueContent || ConfigConstant.OBJ.equals(configData.getValueType().split("#")[1])) {
            ConfigValuePo configValuePo = ConfigValue.builder()
                    .id(configValue.getId())
                    .delFlag(DbConstants.DEL_FLAG1)
                    .build()
                    .toConfigValuePo();
            configValueRepo.updateById(configValuePo);
        } else {
            Class<?> clazz = getConfigValueTypeClass(configData);
            List<Object> newList = new ArrayList<>();
            List<?> delList = JsonUtil.toBeanList(valueContent, clazz);
            Map<String, Object> delMap = new HashMap<>();
            delList.forEach(delObj -> {
                JSONObject delBean = JsonUtil.toBean(delObj, JSONObject.class);
                delMap.put(delBean.getStr(ConfigConstant.CO_ID), delObj);
            });
            List<?> oldList = JsonUtil.toBeanList(configValue.getValueContent(), clazz);
            oldList.forEach(oldObj -> {
                JSONObject oldBean = JsonUtil.toBean(oldObj, JSONObject.class);
                if (delMap.get(oldBean.getStr(ConfigConstant.CO_ID)) == null) {
                    newList.add(oldObj);
                }
            });
            configValue.setValueContent(JsonUtil.toJson(newList));
            configValueRepo.updateById(configValue.toConfigValuePo());
        }
        saveOrCleanConfigValueCache(configValue.getId(), configData);
        return true;
    }

    @Override
    public Boolean updateConfigValue(ConfigValue configValue, ConfigData configData) {
        if (checkConfigValue(configValue, configData)) {
            ConfigValue oldConfigValue = getConfigValue(configValue.getId());
            if (oldConfigValue == null) {
                log.warn("配置数据不存在,configValue={}！", JsonUtil.toJson(configValue));
                throw new BizException("配置数据不存在！");
            }
            //configDataId和valueKey不可修改
            configValue.setConfigDataId(oldConfigValue.getConfigDataId());
            configValue.setValueKey(oldConfigValue.getValueKey());
            //保存数据
            addConfigValueToDb(configValue, configData);
            saveOrCleanConfigValueCache(configValue.getId(), configData);
            return true;
        }
        return false;
    }

    @Override
    public ConfigValue getConfigValue(Long id) {
        ConfigValuePo configValuePo = configValueRepo.getById(id);
        if (configValuePo != null && DbConstants.DEL_FLAG0.equals(configValuePo.getDelFlag())) {
            return ConfigValue.toConfigValue(configValuePo);
        }
        return null;
    }

    @Override
    public PageUtil.PageVo pageConfigValue(ConfigValue configValue) {
        return configValueRepo.pageConfigValue(configValue);
    }

    @Override
    public Long getConfigValueCount(Long configDataId) {
        return configValueRepo.getConfigValueCount(configDataId);
    }


    private boolean checkConfigValue(ConfigValue configValue, ConfigData configData) {
        if (configData == null) {
            log.warn("configDataId对应的数据不存在,configValue={}！", JsonUtil.toJson(configValue));
            throw new BizException("configDataId对应的数据不存在！");
        }
        if (!DataTypeEnum.HASH.getDataType().equals(configData.getDataType())) {
            configValue.setValueKey(null);
        }
        if (DataTypeEnum.HASH.getDataType().equals(configData.getDataType()) && StrUtil.isBlank(configValue.getValueKey())) {
            log.warn("hash类型数据valueKey不能为空,configValue={}！", JsonUtil.toJson(configValue));
            throw new BizException("hash类型数据valueKey不能为空！");
        }
        ConfigValue configValueByConfigDataIdAndValueKey = getConfigValueByConfigDataIdAndValueKey(configValue.getConfigDataId(), configValue.getValueKey());
        if (configValueByConfigDataIdAndValueKey != null && !configValueByConfigDataIdAndValueKey.getId().equals(configValue.getId())) {
            log.warn("valueKey已经存在,configValue={}！", JsonUtil.toJson(configValue));
            throw new BizException("valueKey已经存在！");
        }
        boolean checkResult = checkValueContent(configValue, configData.getDataType(), configData.getValueType());
        if (!checkResult) {
            log.warn("valueContent内容格式不正确,configValue={}！", JsonUtil.toJson(configValue));
            throw new BizException("valueContent内容格式不正确！");
        }
        return true;
    }

    private boolean checkValueContent(ConfigValue configValue, String dataType, String valueType) {
        //验证配置数据内容
        String[] valueTypes = valueType.split("#");
        String co = valueTypes[0];
        String coType = valueTypes[1];
        //四种数据类型, 字符串:"aaa" ,字符串数组: ["aaa"] , 对象：{"":""}, 对象数组：[{"":""}]
        if (ValueTypeEnum.OBJ.getValueType().equals(coType)) {
            if (ConfigConstant.STR.equals(co)) {
                return configValue.getValueContent() instanceof String;
            } else if (ConfigConstant.NUM.equals(co)) {
                return configValue.getValueContent() instanceof Number;
            }
            return configValue.getValueContent() instanceof LinkedHashMap<?, ?>;
        } else if (ValueTypeEnum.LIST.getValueType().equals(coType) || ValueTypeEnum.SET.getValueType().equals(coType)) {
            if (ConfigConstant.STR.equals(co)) {
                return JsonUtil.isJsonStrList(JsonUtil.toJson(configValue.getValueContent()));
            } else if (ConfigConstant.NUM.equals(co)) {
                return JsonUtil.isJsonNumList(JsonUtil.toJson(configValue.getValueContent()));
            }
            return JsonUtil.isJsonObjList(JsonUtil.toJson(configValue.getValueContent()));
        }
        return true;
    }

    private ConfigValue getConfigValueByConfigDataIdAndValueKey(Long configDataId, String valueKey) {
        return ConfigValue.toConfigValue(configValueRepo.getConfigValueTypePoByConfigDataIdAndValueKey(configDataId, valueKey));
    }

    private Long addConfigValueToDb(ConfigValue configValue, ConfigData configData) {
        ConfigValuePo configValuePo;
        if (!configData.getValueType().contains("#" + ConfigConstant.OBJ)) {
            List<?> valueContentList = getValueContentListData(configValue, configData);
            valueContentList = distinctValueContentList(valueContentList, configData);
            configValue.setValueContent(JsonUtil.toJson(valueContentList));
        }
        configValuePo = configValue.toConfigValuePo();
        configValueRepo.saveOrUpdate(configValuePo);
        assert configValuePo != null;
        return configValuePo.getId();
    }

    private List<?> distinctValueContentList(List<?> valueContentList, ConfigData configData) {
        //set集合需要去重
        if (configData.getValueType().contains("#" + ConfigConstant.SET)) {
            List<Object> valueContentListDataDistinct = new ArrayList<>();
            if (configData.getValueType().contains(ConfigConstant.OBJ + "#")) {
                Map<String, Object> distinctValueContentMap = new LinkedHashMap<>();
                for (Object obj : valueContentList) {
                    JSONObject jsonObj = JsonUtil.toBean(obj, JSONObject.class);
                    String coId = jsonObj.getStr(ConfigConstant.CO_ID);
                    if (coId == null) {
                        coId = UUID.randomUUID().toString();
                        jsonObj.set(ConfigConstant.CO_ID, coId);
                    }
                    distinctValueContentMap.put(coId, obj);
                }
                valueContentListDataDistinct.addAll(distinctValueContentMap.values());
            } else {
                for (Object obj : valueContentList) {
                    if (!valueContentListDataDistinct.contains(obj)) {
                        valueContentListDataDistinct.add(obj);
                    }
                }
            }
            return valueContentListDataDistinct;
        }
        return valueContentList;
    }

    private void saveOrCleanConfigValueCache(Long id, ConfigData configData) {
        ConfigValuePo configValuePo = configValueRepo.getById(id);
        String dataType = configData.getDataType();
        String dataKey = configData.getDataKey();
        if (configValuePo.getDelFlag().equals(DbConstants.DEL_FLAG1)) {
            if (dataType.equals(ConfigConstant.STR)) {
                redisService.getRedissonClient(RedisConstant.CLUSTER1).getBucket(dataKey, new StringCodec()).delete();
            } else if (dataType.equals(ConfigConstant.HASH)) {
                redisService.getRedissonClient(RedisConstant.CLUSTER1).getMap(dataKey, new StringCodec()).remove(configValuePo.getValueKey());
            }
        } else {
            if (dataType.equals(ConfigConstant.STR)) {
                redisService.getRedissonClient(RedisConstant.CLUSTER1).getBucket(dataKey, new StringCodec()).set(configValuePo.getValueContent());
            } else if (dataType.equals(ConfigConstant.HASH)) {
                redisService.getRedissonClient(RedisConstant.CLUSTER1).getMap(dataKey, new StringCodec()).put(configValuePo.getValueKey(), configValuePo.getValueContent());
            }
        }
    }

    private List<Object> getValueContentListData(ConfigValue configValue, ConfigData configData) {
        ConfigValue configValueByConfigDataIdAndValueKey = getConfigValueByConfigDataIdAndValueKey(configValue.getConfigDataId(), configValue.getValueKey());
        Class<?> clazz = getConfigValueTypeClass(configData);
        List<Object> valueContentList = new ArrayList<>();
        if (null != configValueByConfigDataIdAndValueKey) {
            Object valueContent = configValueByConfigDataIdAndValueKey.getValueContent();
            List<?> valueContentHistoryList = JsonUtil.toBeanList(valueContent, clazz);
            valueContentList.addAll(valueContentHistoryList);
        }
        List<?> valueContentNewList = JsonUtil.toBeanList(configValue.getValueContent(), clazz);
        valueContentList.addAll(valueContentNewList);
        return valueContentList;
    }

    private Class<?> getConfigValueTypeClass(ConfigData configData) {
        Class<?> clazz;
        if (configData.getValueType().contains(ConfigConstant.STR + "#")) {
            clazz = String.class;
        } else if (configData.getValueType().contains(ConfigConstant.NUM + "#")) {
            clazz = Number.class;
        } else {
            clazz = Object.class;
        }
        return clazz;
    }

}
